import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { initCameraParams } from '@/views/common'

/**
 * 创建一个球体来显示可视化位置
 * @param {*} pos:需要显示的位置，例如：new THREE.Vector3(24,2,10)
 * @param {*} scene three场景对象
 * @param {*} radius 球体半径，不传入为 0.1
 * @param {*} color 球体颜色，不传入为红色 #ff0000
 */
export function viewPos(pos, scene, radius, color) {
  const geometry = new THREE.SphereGeometry(radius ? radius : 0.1)
  const material = new THREE.MeshBasicMaterial({
    color: color ? color : '#ff0000'
  })
  const mesh = new THREE.Mesh(geometry, material)
  mesh.position.copy(pos)
  scene.add(mesh)
  return mesh
}

/**
 * 创建一个箭头来指示出方向
 * @param {*} end：结束点，箭头将从start指向这里，例如：new THREE.Vector3(24,2,10)
 * @param {*} color 箭头颜色，不传入为红色 #ff0000
 * @param {*} strat 起始点，不传入为原点 new THREE.Vector3(0, 0, 0)
 */
export function viewDir(end, color, strat) {
  let str = strat ? strat : new THREE.Vector3(0, 0, 0)
  const dir = end
    .clone()
    .sub(str)
    .normalize()
  const length = end
    .clone()
    .sub(str)
    .length()
  const arrowHelper = new THREE.ArrowHelper(
    dir,
    str,
    length,
    color ? color : '#ff0000'
  )
  return arrowHelper
}

/**
 * 清除threejs创建的对象，释放对象以及后代的几何体、材质纹理等资源
 * @param {*} obj:需要清除释放的threejs对象
 * @param {*} all:是否清除所有，all为false时只会清理obj后代资源，保留obj对象
 */
export function disposeObject(obj, all = false) {
  if (!obj) return

  const materialMaps = [
    'map',
    'alphaMap',
    'aoMap',
    'bumpMap',
    'displacementMap',
    'emissiveMap',
    'envMap',
    'lightMap',
    'metalnessMap',
    'normalMap',
    'roughnessMap',
    'specularMap'
  ]

  if (obj.traverse) {
    obj.traverse(item => {
      // 释放几何体
      if (item.geometry) {
        item.geometry.dispose()
      }

      // 释放材质和纹理
      if (item.material) {
        if (Array.isArray(item.material)) {
          item.material.forEach(material => {
            materialMaps.forEach(mapName => {
              if (material[mapName]) {
                material[mapName].dispose()
              }
            })
            material.dispose()
          })
        } else {
          materialMaps.forEach(mapName => {
            if (item.material[mapName]) {
              item.material[mapName].dispose()
            }
          })
          item.material.dispose()
        }
      }

      // 清除CSS2D/3D对象dom元素
      if (item.isCSS2DObject || item.isCSS3DObject) {
        if (item.element && item.element.parentNode) {
          item.element.parentNode.removeChild(item.element)
        }
      }
    })
  }
  obj.children = []

  if (all) {
    // 清理自身
    if (obj.dispose) {
      obj.dispose()
    }
    obj = null
  }
}

/**
 * 异步加载gltf模型
 * @param {*} url:模型地址，模型需要放置在public文件下
 */
export function loadGLTFModel(url) {
  return new Promise((resolve, reject) => {
    const loader = new GLTFLoader()
    loader.load(
      url,
      gltf => {
        resolve(gltf.scene)
      },
      undefined,
      error => {
        reject(error)
      }
    )
  })
}

/**
 * 异步加载纹理贴图
 * @param {*} url:纹理贴图地址，纹理贴图需要放置在public文件下
 */
export function loadTexture(url) {
  return new Promise((resolve, reject) => {
    const loader = new THREE.TextureLoader()
    loader.load(
      url,
      texture => {
        resolve(texture)
      },
      undefined,
      error => {
        reject(error)
      }
    )
  })
}

/**
 * 检测一个位置是否在地图模型中，返回检测到的子地图模型
 * @param {*} center:二维位置数组，由经纬度坐标转换而来，例如：[12.31,22.12]
 * @param {*} mapModel:检测的地图模型
 */
export function testMap(center, mapModel, camera) {
  // 创建射线起点和方向
  const rayOrigin = new THREE.Vector3(center[0], 1, center[1])
  const rayDirection = new THREE.Vector3(0, -1, 0)

  // 更新射线起点和方向，根据模型的变换矩阵
  rayOrigin.applyMatrix4(mapModel.matrixWorld)
  rayDirection.transformDirection(mapModel.matrixWorld)

  // 创建射线
  const raycaster = new THREE.Raycaster(rayOrigin, rayDirection)
  raycaster.camera = camera
  // 更新模型的世界矩阵
  mapModel.updateMatrixWorld(true)

  // 射线检测
  let intersects = raycaster.intersectObjects(mapModel.children[0].children)

  // 检测结果筛选
  intersects = intersects.filter(function(intersect) {
    return (
      intersect.object.parent.name !== '边线-发光' &&
      intersect.object.name !== '光圈' &&
      intersect.object.name !== '光柱' &&
      intersect.object.name !== '描边模型' &&
      intersect.object.name !== '合并模型'
    )
  })
  return intersects
}

export function calculateModelSize(object) {
  const box = new THREE.Box3().setFromObject(object)
  const size = new THREE.Vector3()
  box.getSize(size)
  return size
}

// 根据区域编码判断行政管理级别
export function getAdministrativeLevel(code) {
  const cityCode = code.substring(2, 4)
  const districtCode = code.substring(4, 6)

  if (code === '100000') {
    return '国'
  } else if (cityCode === '00' && districtCode === '00') {
    return '省'
  } else if (districtCode === '00') {
    return '市'
  } else {
    return '县'
  }
}

export function resetOrigin(model, offset, status) {
  const scaledBox = new THREE.Box3().setFromObject(model)
  const center = new THREE.Vector3()
  scaledBox.getCenter(center)
  if (offset) {
    for (let i in offset) {
      center[i] += offset[i]
    }
  }
  model.position.sub(center)
  if (status == '省') {
    model.position.y += center.y
  } else {
    model.position.y = 0
  }
}

export function setCameraPosition(cameraParams, camera, controls) {
  const yawAngle = THREE.MathUtils.degToRad(cameraParams.yawAngle)
  const highAngle = THREE.MathUtils.degToRad(cameraParams.highAngle)
  camera.position.x =
    cameraParams.height * Math.cos(highAngle) * Math.sin(yawAngle)
  camera.position.y = cameraParams.height * Math.sin(highAngle)
  camera.position.z =
    cameraParams.height * Math.cos(highAngle) * Math.cos(yawAngle)
  camera.position.x += cameraParams.offsetX
  camera.position.y += 0
  camera.position.z += cameraParams.offsetZ
  controls.target.set(cameraParams.offsetX, 0, cameraParams.offsetZ)
  controls.update()
}

// 重置相机配置参数
export function resetCameraParams(cameraParams, camera, controls) {
  cameraParams = initCameraParams
  setCameraPosition(cameraParams, camera, controls)
}
